#include "client.h"

#include <v8.h>
#include <memory>
#include<iostream>
#include <thread>
#include <iostream> 
#include <fstream> 
#include <iterator> 
#include <string> 
#include <vector> 
#include <sstream>
#include <algorithm>

#include "utils/utils.h"
#include "utils/hlog.h"
#include "utils/helper.h"

#include <iconv.h>
#include<stdio.h> 
#include<unistd.h> 
#define MKDIR(a) mkdir((a),0755)
#define ACCESS access 
#ifdef __APPLE__
#import <Foundation/Foundation.h>
#import <AVFoundation/AVFoundation.h>
#import <CoreGraphics/CoreGraphics.h>
#import <AVKit/AVKit.h>
#import <libproc.h>
//#import <Cocoa/Cocoa.h>
#endif // __APPLE__

namespace privacy_service
{
using namespace v8;
using namespace node;
using PersistentPtr = std::shared_ptr<v8::Global<v8::Function> >;
Persistent<Function> Client::constructor;

Client::Client()
{    
	channel_ = new NodeChannel;
	channel_->buildConnect();
}

Client::~Client() 
{
	hlogd("Client::~Client");
	channel_->removeConnect();
	delete channel_;
}

void Client::init(Local<Object> exports) 
{
	Isolate* isolate = exports->GetIsolate();
	//准备构造函数模板
	Local<FunctionTemplate> tp = FunctionTemplate::New(isolate, instance);
#ifdef ELECTRON_8
	v8::Local<v8::String> value = v8::String::NewFromUtf8(isolate, "client", v8::NewStringType::kNormal).ToLocalChecked();
	tp->SetClassName(value);
#else
	tp->SetClassName(String::NewFromUtf8(isolate, "client"));
#endif
	tp->InstanceTemplate()->SetInternalFieldCount(1);
	//导出函数的原型
	NODE_SET_PROTOTYPE_METHOD(tp, "initPath", initPath);
	NODE_SET_PROTOTYPE_METHOD(tp, "getScreenRecordingAccess", getScreenRecordingAccess);
	NODE_SET_PROTOTYPE_METHOD(tp, "getCameraAccess", getCameraAccess);
	NODE_SET_PROTOTYPE_METHOD(tp, "getMicAccess", getMicAccess);
	NODE_SET_PROTOTYPE_METHOD(tp, "getAccessibility", getAccessibility);
#ifdef NODE_12
	v8::Local<Context> context = isolate->GetCurrentContext();
	exports->Set(String::NewFromUtf8(isolate, "client"), tp->GetFunction(context).ToLocalChecked());
	constructor.Reset(isolate, tp->GetFunction(context).ToLocalChecked());
#elif defined ELECTRON_8
	v8::Local<Context> context = isolate->GetCurrentContext();
	exports->Set(context,String::NewFromUtf8(isolate, "client").ToLocalChecked(), tp->GetFunction(context).ToLocalChecked());
	constructor.Reset(isolate, tp->GetFunction(context).ToLocalChecked());
#else
	exports->Set(String::NewFromUtf8(isolate, "client"), tp->GetFunction());
	constructor.Reset(isolate, tp->GetFunction());
#endif
}

void Client::instance(const FunctionCallbackInfo<Value> &args)
{
	Isolate* isolate = args.GetIsolate();
	if (args.IsConstructCall()) 
	{
		// Invoked as constructor: `new MyObject(...)`
		//// 像构造函数一样调用：`new MyObject(...)`
#ifdef NODE_12
		v8::Local<v8::Context> context = isolate->GetCurrentContext();
		double value = args[0]->IsUndefined() ? 0 : args[0]->NumberValue(context).ToChecked();
#else
		double value = args[0]->IsUndefined() ? 0 : args[0]->NumberValue();
#endif
		Client* client = new Client;
		client->Wrap(args.This());
		client->Ref();
		args.GetReturnValue().Set(args.This());
	}
	else 
	{
		// Invoked as plain function `MyObject(...)`, turn into construct call.
		// 像普通方法 `MyObject(...)` 一样调用，转为构造调用。
		const int argc = 1;
		Local<Value> argv[argc] = { args[0] };
		Local<Context> context = isolate->GetCurrentContext();
		Local<Function> cons = Local<Function>::New(isolate, constructor);
		Local<Object> result = cons->NewInstance(context, argc, argv).ToLocalChecked();
		args.GetReturnValue().Set(result);
	}
}

void Client::initPath(const v8::FunctionCallbackInfo<v8::Value> &args)
{
#ifdef _APPLE_
#else
	Isolate* isolate = args.GetIsolate();
	Client* client = ObjectWrap::Unwrap<Client>(args.Holder());
	//把参数转换成本地对象
	Local<Object> info = Local<Object>::Cast(args[0]);
	std::string logPath = Helper::toCppStringFromObj(info, "logPath");
	logPath = logPath + "/tray_node.log";
	bool ret = false;
#ifdef _WIN32
	std::wstring logPathws;
	logPathws = SToWS(logPath);
	if (ACCESS(logPathws.c_str(), 0) == 0)
	{
		ret = true;
	}
#else
	if (ACCESS(logPath.c_str(), 0) == 0)
	{
		ret = true;
	}
#endif // _WIN32
	else
	{
		if (creatDir((char*)logPath.c_str()) == 0)
		{
			ret = true;
		}
	}
	hlog_set_file(logPath.c_str());
	hlogd("logPath=%s", logPath.c_str());
#endif // _APPLE_
}
#ifdef __APPLE__
bool CanRecordScreenFunc() {
 	CFArrayRef windowList = CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly, kCGNullWindowID);
	NSUInteger numberOfWindows = CFArrayGetCount(windowList);
	NSUInteger numberOfWindowsWithName = 0;
	for (NSUInteger idx = 0; idx < numberOfWindows; idx++) {
		NSDictionary *windowInfo = (NSDictionary *)CFArrayGetValueAtIndex(windowList, idx);
		NSString *windowName = windowInfo[(id)kCGWindowName];
		if (windowName) {
			numberOfWindowsWithName++;
		}
		else {
			break;
		}
	}
	CFRelease(windowList);
	return numberOfWindows == numberOfWindowsWithName; 
}

//获取摄像头授权状态
int getCameraAuthorizationStatus()
{
    int authStatus = AVAuthorizationStatusAuthorized;
    if (@available(macOS 10.14, *))//mac10.14及以上才有设备授权
    {
        authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
 
    }
    return authStatus;
}
 
 
//获取麦克风授权状态
int getMicAuthorizationStatus()
{
    int authStatus = AVAuthorizationStatusAuthorized;
    if (@available(macOS 10.14, *))//mac10.14及以上才有设备授权
    {
        authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio];
    }
    return authStatus;
}

bool isScreenRecordingAuthorized()
{
  bool auth_status = false;
  if (@available(macOS 10.15, *)) {
    NSRunningApplication *runningApplication =
        NSRunningApplication.currentApplication;
    NSNumber *ourProcessIdentifier =
        [NSNumber numberWithInteger:runningApplication.processIdentifier];

    CFArrayRef windowList =
        CGWindowListCopyWindowInfo(kCGWindowListOptionAll, kCGNullWindowID);
    int numberOfWindows = CFArrayGetCount(windowList);
    for (int index = 0; index < numberOfWindows; index++) {
      // Get information for each window.
      NSDictionary *windowInfo =
          (NSDictionary *)CFArrayGetValueAtIndex(windowList, index);
      NSString *windowName = windowInfo[(id)kCGWindowName];
      NSNumber *processIdentifier = windowInfo[(id)kCGWindowOwnerPID];

      // Don't check windows owned by the current process.
      if (![processIdentifier isEqual:ourProcessIdentifier]) {
        // Get process information for each window.
        pid_t pid = processIdentifier.intValue;
        NSRunningApplication *windowRunningApplication =
            [NSRunningApplication runningApplicationWithProcessIdentifier:pid];
        if (windowRunningApplication) {
          NSString *windowExecutableName =
              windowRunningApplication.executableURL.lastPathComponent;
          if (windowName) {
            if (![windowExecutableName isEqual:@"Dock"]) {
              auth_status = true;
              break;
            }
          }
        }
      }
    }
    CFRelease(windowList);
  } else {
    auth_status = true;
  }

  return auth_status;
}

//申请摄像头权限
void requestCameraAccess(std::function<void(BOOL,int)>cb)
{
    int authStatus = getCameraAuthorizationStatus();//获取授权状态
    if (AVAuthorizationStatusNotDetermined == authStatus) //若用户尚未选择关于客户端是否可以访问硬件，则申请权限
    {
        //向系统申请权限，系统会弹出授权询问弹框
        [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted)
        {
            //do something，可以判断granted的值做相应处理，该值存储了系统弹框的选择结果
			cb(granted,authStatus);
        }];
    }
    else if(AVAuthorizationStatusAuthorized == authStatus){
			cb(true,authStatus);
		}
	else {
		cb(false,authStatus);
	}
}
 
 
//申请麦克风权限
void requestMicAccess(std::function<void(BOOL,int)>cb)
{
    int authStatus = getMicAuthorizationStatus();//获取授权状态
    if (AVAuthorizationStatusNotDetermined == authStatus) //若用户尚未选择关于客户端是否可以访问硬件，则申请权限
    {
        //向系统申请权限，系统会弹出授权询问弹框
        [AVCaptureDevice requestAccessForMediaType:AVMediaTypeAudio  completionHandler:^(BOOL granted) {
            //do something，可以判断granted的值做相应处理，该值存储了系统弹框的选择结果
						cb(granted,authStatus);
        }];
    }
    else if(AVAuthorizationStatusAuthorized == authStatus){
			cb(true,authStatus);
   }
  else{
		cb(false,authStatus);
	}
}
 
 
//申请屏幕录制权限
bool requestScreenRecordingAccess()
{
    bool isAuthorized = isScreenRecordingAuthorized();//获取授权状态
    if (!isAuthorized)//未授权则申请
    {
        //向系统申请权限，系统会弹出授权询问弹框
        CGImageRef c = CGWindowListCreateImage(CGRectMake(0, 0, 1, 1),
                                               kCGWindowListOptionOnScreenOnly,
                                               kCGNullWindowID,
                                               kCGWindowImageDefault);
        CFRelease(c);
    }
	return isAuthorized;
}

//打开隐私设置页面并选中摄像头选项
void openPrivacyCameraSettingPane()
{
    NSString *urlString = @"x-apple.systempreferences:com.apple.preference.security?Privacy_Camera";
 
    [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:urlString]];
}
 
//打开隐私设置页面并选中麦克风选项
void openPrivacyMicSettingPane()
{
    NSString *urlString = @"x-apple.systempreferences:com.apple.preference.security?Privacy_Microphone";
 
    [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:urlString]];
}
 
//打开隐私设置页面并选中屏幕录制选项
void openPrivacyScreenRecordingSettingPane()
{
    NSString *urlString = @"x-apple.systempreferences:com.apple.preference.security?Privacy_ScreenCapture";
 
    [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:urlString]];
}
//打开隐私设置页面并选中辅助功能选项
void openAccessibilityAccess() {
  NSDictionary *options = @{(id)kAXTrustedCheckOptionPrompt : @(NO)};
  bool trusted = AXIsProcessTrustedWithOptions((CFDictionaryRef)options);

  if (!trusted) {
    NSString *urlString = @"x-apple.systempreferences:com.apple.preference."
                            @"security?Privacy_Accessibility";
    [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:urlString]];
  }
}

void getCameraAccessHandle(void* args)
{
	camera_access_data *data = (camera_access_data *)args;
	requestCameraAccess([data](BOOL granted,int authStatus){
		if(!granted){
				openPrivacyCameraSettingPane();
		}
		std::string backMsg;
		JSON_Value* pJson = json_value_init_object();
		if (pJson != NULL) {
			JSON_Object* root_object = json_value_get_object(pJson);
			if (root_object != NULL) {
				json_object_set_boolean(root_object, "privacyCamera", granted);
			}
			MAKE_BACKMSG();
		}
		data->channel->callMainThread(backMsg, data->cb);
	});
}

void getMicAccessHandle(void* args)
{
	mic_access_data *data = (mic_access_data *)args;
	requestMicAccess([data](BOOL granted,int authStatus){
		if(!granted){
				openPrivacyMicSettingPane();
		}
		std::string backMsg;
		JSON_Value* pJson = json_value_init_object();
		if (pJson != NULL) {
			JSON_Object* root_object = json_value_get_object(pJson);
			if (root_object != NULL) {
				json_object_set_boolean(root_object, "privacyMic", granted);
			}
			MAKE_BACKMSG();
		}
		data->channel->callMainThread(backMsg, data->cb);
	});
}

#endif
void Client::getMicAccess(const v8::FunctionCallbackInfo<v8::Value> &args)
{
#ifdef __APPLE__
	GET_ISOlATE();
	Client* _client = ObjectWrap::Unwrap<Client>(args.Holder());
	_client->mic_access_data_.channel = _client->channel_;
	_client->mic_access_data_.cb = Helper::persistentFunc(args[1]);
	std::thread thread(getMicAccessHandle, &_client->mic_access_data_);
	thread.detach();
#endif // __APPLE__
}

void Client::getCameraAccess(const v8::FunctionCallbackInfo<v8::Value> &args)
{
#ifdef __APPLE__
	GET_ISOlATE();
	Client* _client = ObjectWrap::Unwrap<Client>(args.Holder());
	_client->camera_access_data_.channel = _client->channel_;
	_client->camera_access_data_.cb = Helper::persistentFunc(args[1]);
	std::thread thread(getCameraAccessHandle, &_client->camera_access_data_);
	thread.detach();
#endif // __APPLE__
}

void Client::getScreenRecordingAccess(const v8::FunctionCallbackInfo<v8::Value> &args)
{
#ifdef __APPLE__
	GET_ISOlATE();
	Client* _client = ObjectWrap::Unwrap<Client>(args.Holder());
	bool screenRecordFlag = false;
	screenRecordFlag = requestScreenRecordingAccess();
	if(!screenRecordFlag)
	{
		openPrivacyScreenRecordingSettingPane();
	}	
	args.GetReturnValue().Set(screenRecordFlag);

#endif // __APPLE__
}

void Client::getAccessibility(const v8::FunctionCallbackInfo<v8::Value> &args)
{
	#ifdef __APPLE__
	GET_ISOlATE();
	Client* _client = ObjectWrap::Unwrap<Client>(args.Holder());
	bool accessFlag = false;
	accessFlag = AXIsProcessTrusted();
	if(!accessFlag)
	{
		openAccessibilityAccess();
	}	
	args.GetReturnValue().Set(accessFlag);

#endif // __APPLE__
}



} /*namespace privacy_service*/

